#include "../scanner/scanner.h"
#include "../storage/storage.h"
#include "../core/servicelocator.h"
#include "DirectoryMock.h"
#include "StorageFactoryMock.h"
#include "StorageMock.h"
#include "gmock/gmock.h"
#include "FileMock.h"
#include <auto_ptr.h>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

using namespace storagemon;
using namespace boost::filesystem;
using ::testing::_;
using ::testing::Return;
using ::testing::StrEq;


class ScannerTestBase : public ::testing::Test
{
public:
    void SetUp()
    {
        m_sl.reset(new ServiceLocator(0));

        m_storageFactory.reset(new mock::StorageFactoryMock());

        m_sl.get()->RegisterService(STORAGE_FACTORY, m_storageFactory.get());
    }

    void TearDown()
    {
        m_sl.get()->UnregisterService(STORAGE_FACTORY);
        if (exists("test_folder"))
            remove_all("test_folder");

        for (std::vector<IDirectory*>::iterator it = m_dirList.begin();
                it != m_dirList.end(); ++it)
        {
            delete (*it);
        }
    }

    void PrepareTestFolder()
    {
        path root = current_path();
        path test_file1(path("test_folder") / path("test1")/ path("test2") / "testfile.txt");
        path test_file2(path("test_folder") / path("test3")/ path("test4") / "testfile2.txt");
        remove_all("test_folder");
        create_directories(test_file1.branch_path());
        create_directories(test_file2.branch_path());
        fstream file;
        file.open(test_file1, std::ios::out);
        file << "test string";
        file.close();
        file.open(test_file2, std::ios::out);
        file << "test string";
        file.close();
    }

    void ConfigureScanFolderExpectation()
    {
        scan_path.push_back(std::string(current_path().directory_string()).append("/")
        .append("test_folder"));
        m_storage.reset(new mock::StorageObjectMock());
        m_scanner.reset(new Scanner(m_storage.get(), m_sl.get()));
        m_directory.reset(new mock::DirectoryObjectMock());

        EXPECT_CALL(*(m_storage.get()),
            path())
        .Times(1)
        .WillOnce(Return(scan_path[0].c_str()));

        EXPECT_CALL(*(m_storage.get()),
            add_dir(_))
        .Times(5)
        .WillRepeatedly(Return(true));

        EXPECT_CALL(*(m_storageFactory.get()),
            CreateService(StrEq(DIRECTORYOBJECT), StrEq("test_folder"), _, 0, 0, _))
        .Times(1)
        .WillOnce(Return(static_cast<IService*>(m_directory.get())));

        EXPECT_CALL(*(m_storageFactory.get()),
            CreateService(StrEq(DIRECTORYOBJECT), StrEq("test1"), _, 1, 0, _))
        .Times(1)
        .WillOnce(Return(static_cast<IService*>(m_directory.get())));

        EXPECT_CALL(*(m_storageFactory.get()),
            CreateService(StrEq(DIRECTORYOBJECT), StrEq("test2"), _, 2, 1, _))
        .Times(1)
        .WillOnce(Return(static_cast<IService*>(m_directory.get())));

        EXPECT_CALL(*(m_storageFactory.get()),
            CreateService(StrEq(DIRECTORYOBJECT), StrEq("test3"), _, 3, 0, _))
        .Times(1)
        .WillOnce(Return(static_cast<IService*>(m_directory.get())));

        EXPECT_CALL(*(m_storageFactory.get()),
            CreateService(StrEq(DIRECTORYOBJECT), StrEq("test4"), _, 4, 3, _))
        .Times(1)
        .WillOnce(Return(static_cast<IService*>(m_directory.get())));
    }

    void ConfigureScanFilesExpectation()
    {
        m_file.reset(new mock::FileObjectMock);
        m_dirList.push_back(new mock::DirectoryObjectMock);
        m_dirList.push_back(new mock::DirectoryObjectMock);
        m_dirList.push_back(new mock::DirectoryObjectMock);
        m_dirList.push_back(new mock::DirectoryObjectMock);
        m_dirList.push_back(new mock::DirectoryObjectMock);
        scan_path.clear();
        scan_path.push_back(std::string(current_path().directory_string()).append("/")
        .append("test_folder"));
        scan_path.push_back(std::string(scan_path[0].c_str()).append("/")
        .append("test1"));
        scan_path.push_back(std::string(scan_path[1].c_str()).append("/")
        .append("test2"));
        scan_path.push_back(std::string(scan_path[0].c_str()).append("/")
        .append("test3"));
        scan_path.push_back(std::string(scan_path[3].c_str()).append("/")
        .append("test4"));

        EXPECT_CALL(*(m_storage.get()),
            size())
            .Times(2)
            .WillRepeatedly(Return(5));

        EXPECT_CALL(*(m_storage.get()), get_dir( _ ))
            .Times(5)
            .WillOnce(Return(m_dirList.begin()))
            .WillOnce(Return(m_dirList.begin()+1))
            .WillOnce(Return(m_dirList.begin()+2))
            .WillOnce(Return(m_dirList.begin()+3))
            .WillOnce(Return(m_dirList.begin()+4));

        for (int i = 0; i != m_dirList.size(); i++)
        {
            EXPECT_CALL(*(static_cast<mock::DirectoryObjectMock*>(m_dirList[i])),
                    path())
                .Times(1)
                .WillRepeatedly(Return(scan_path[i].c_str()));
        }

        EXPECT_CALL(*(static_cast<mock::DirectoryObjectMock*>(m_dirList[2])), id())
                .Times(1)
                .WillRepeatedly(Return(2));

        EXPECT_CALL(*(static_cast<mock::DirectoryObjectMock*>(m_dirList[2])), add_file( _ ))
                .Times(1)
                .WillRepeatedly(Return(true));

        EXPECT_CALL(*(static_cast<mock::DirectoryObjectMock*>(m_dirList[4])), id())
                .Times(1)
                .WillRepeatedly(Return(4));

        EXPECT_CALL(*(static_cast<mock::DirectoryObjectMock*>(m_dirList[4])), add_file( _ ))
                .Times(1)
                .WillRepeatedly(Return(true));

        EXPECT_CALL(*(m_storageFactory.get()),
                CreateService(StrEq(FILEOBJECT), _, StrEq("testfile.txt"), 0, 2, _))
            .Times(1)
            .WillOnce(Return(static_cast<IService*>(m_file.get())));

        EXPECT_CALL(*(m_storageFactory.get()),
                CreateService(StrEq(FILEOBJECT), _, StrEq("testfile2.txt"), 1, 4, _))
            .Times(1)
            .WillOnce(Return(static_cast<IService*>(m_file.get())));

        EXPECT_CALL(*(m_file.get()), full_path())
            .Times(2)
            .WillRepeatedly(Return(""));

        EXPECT_CALL(*(m_file.get()), md5_string( _ ))
            .Times(2)
            .WillRepeatedly(Return(const_cast<char*>(md5.c_str())));
    }
public:
    std::auto_ptr<IServiceLocator> m_sl;
    std::auto_ptr<mock::StorageFactoryMock> m_storageFactory;
    std::auto_ptr<mock::StorageObjectMock> m_storage;
    std::auto_ptr<Scanner> m_scanner;
    std::auto_ptr<mock::DirectoryObjectMock> m_directory;
    std::auto_ptr<mock::FileObjectMock> m_file;
    std::vector<IDirectory*> m_dirList;
    std::vector<std::string> scan_path;
    std::string md5;
};


TEST_F(ScannerTestBase, ScanFolder)
{
    PrepareTestFolder();
    ConfigureScanFolderExpectation();
    ASSERT_TRUE(m_scanner.get()->Tree());
}

TEST_F(ScannerTestBase, ScanFiles)
{
    PrepareTestFolder();
    ConfigureScanFolderExpectation();
    ASSERT_TRUE(m_scanner.get()->Tree());
    ConfigureScanFilesExpectation();
    ASSERT_TRUE(m_scanner.get()->Scan());
}

